#include "ucalendarwidget.h"
#include "uapplication.h"
#include "ulabel.h"
#include "ustylebutton.h"
#include "upushbutton.h"
#include "style/uproxystyle.h"
#include "style/upalette.h"
#include "udatetimeedit.h"

#include <QEvent>
#include <QHBoxLayout>
#include <QLocale>
#include <QPainter>
#include <QPainterPath>
#include <QSpinBox>
#include <QTextCharFormat>
#include <QTimer>

class UCalendarWidgetPrivate
{
public:
    UCalendarWidgetPrivate(UCalendarWidget* q) : q_ptr(q) {}
    ~UCalendarWidgetPrivate() {}

    void setTextFormat();

    void initStyleSheet();

    void initNavigationBar();

    void initButtomWidget();

    void initConnnection();

    void setDataTimeText(int year, int month);

private:
    Q_DECLARE_PUBLIC(UCalendarWidget)
    Q_DISABLE_COPY(UCalendarWidgetPrivate)
    UCalendarWidget* const q_ptr = nullptr;
    UStyleButton* leftYearBtn = nullptr;
    UStyleButton* leftMonthBtn = nullptr;
    UStyleButton* rightYearBtn = nullptr;
    UStyleButton* rightMonthBtn = nullptr;
    ULabel* yearLabel = nullptr;
    ULabel* yearText = nullptr;
    ULabel* monthLabel = nullptr;
    ULabel* monthText = nullptr;
    ULabel* timeLabel = nullptr;
    UTimeEdit* dateTimeEdit = nullptr;
    UPushButton* confirm = nullptr;
    int currentMonth = 0;
    QTimer timer;
};

void UCalendarWidgetPrivate::setTextFormat()
{
    Q_Q(UCalendarWidget);
    QTextCharFormat format;
    format.setForeground(q->palette().color(QPalette::WindowText));
    format.setBackground(q->palette().color(QPalette::Window));
    q->setHeaderTextFormat(format);
    q->setWeekdayTextFormat(Qt::Saturday, format);
    q->setWeekdayTextFormat(Qt::Sunday, format);
    q->setWeekdayTextFormat(Qt::Monday, format);
    q->setWeekdayTextFormat(Qt::Tuesday, format);
    q->setWeekdayTextFormat(Qt::Wednesday, format);
    q->setWeekdayTextFormat(Qt::Thursday, format);
    q->setWeekdayTextFormat(Qt::Friday, format);
}

void UCalendarWidgetPrivate::initStyleSheet()
{
    Q_Q(UCalendarWidget);

    q->setAutoFillBackground(false);
    q->setContentsMargins(1, 1, 1, 1);
    q->layout()->setSizeConstraint(QLayout::SetFixedSize);
    q->setLocale(QLocale(uApp->currentLanguage()));
    q->setNavigationBarVisible(false);
    q->setVerticalHeaderFormat(QCalendarWidget::NoVerticalHeader);
    q->setHorizontalHeaderFormat(QCalendarWidget::ShortDayNames);
    setTextFormat();
    q->setDateEditEnabled(false);
    timer.start(1000);
}

void UCalendarWidgetPrivate::initNavigationBar()
{
    Q_Q(UCalendarWidget);
    QWidget* navigationBar = new QWidget(q);
    navigationBar->setObjectName("CalendarNavigationBar");
    navigationBar->setFixedHeight(40);
    navigationBar->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);

    QHBoxLayout* hboxLayout = new QHBoxLayout;
    hboxLayout->setContentsMargins(12, 0, 12, 0);
    hboxLayout->setSpacing(4);

    leftYearBtn = new UStyleButton(q);
    leftYearBtn->setFlat(true);
    leftYearBtn->setIcon(QIcon::fromTheme("leftarrow"));
    leftYearBtn->setObjectName("CalendarLeftYearBtn");

    leftMonthBtn = new UStyleButton(q);
    leftMonthBtn->setFlat(true);
    leftMonthBtn->setIcon(QIcon::fromTheme("left"));
    leftMonthBtn->setObjectName("CalendarLeftMonthBtn");

    rightYearBtn = new UStyleButton(q);
    rightYearBtn->setFlat(true);
    rightYearBtn->setIcon(QIcon::fromTheme("rightarrow"));
    rightYearBtn->setObjectName("CalendarRightYearBtn");

    rightMonthBtn = new UStyleButton(q);
    rightMonthBtn->setFlat(true);
    rightMonthBtn->setIcon(QIcon::fromTheme("right"));
    rightMonthBtn->setObjectName("CalendarRightMonthBtn");

    yearLabel = new ULabel(q);
    yearLabel->setText(QObject::tr("year"));
    yearText = new ULabel(q);
    yearText->setObjectName("CalendarYearLabel");
    monthLabel = new ULabel(q);
    monthLabel->setText(QObject::tr("month"));
    monthText = new ULabel(q);
    monthText->setObjectName("CalendarMonthLabel");

    leftYearBtn->setFixedSize(24, 24);
    leftMonthBtn->setFixedSize(24, 24);
    rightYearBtn->setFixedSize(24, 24);
    rightMonthBtn->setFixedSize(24, 24);

    hboxLayout->addWidget(leftYearBtn);
    hboxLayout->addWidget(leftMonthBtn);
    hboxLayout->addStretch();
    hboxLayout->addWidget(yearText);
    hboxLayout->addWidget(yearLabel);
    hboxLayout->addWidget(monthText);
    hboxLayout->addWidget(monthLabel);
    hboxLayout->addStretch();
    hboxLayout->addWidget(rightMonthBtn);
    hboxLayout->addWidget(rightYearBtn);
    navigationBar->setLayout(hboxLayout);

    QVBoxLayout* vBodyLayout = qobject_cast<QVBoxLayout*>(q->layout());
    vBodyLayout->insertWidget(0, navigationBar);

    setDataTimeText(q->selectedDate().year(), q->selectedDate().month());
}

void UCalendarWidgetPrivate::initButtomWidget()
{
    Q_Q(UCalendarWidget);
    QWidget* buttomWidget = new QWidget(q);
    buttomWidget->setObjectName("CalendarDateTimeWidget");
    buttomWidget->setFixedHeight(40);
    buttomWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);

    timeLabel = new ULabel(q);
    timeLabel->setFixedHeight(22);
    timeLabel->setText(QObject::tr("time"));

    dateTimeEdit = new UTimeEdit(QTime::currentTime(), q);
    dateTimeEdit->setRadius(2);
    dateTimeEdit->setBorderWidth(0.0);
    dateTimeEdit->setButtonSymbols(QSpinBox::NoButtons);
    dateTimeEdit->setMinimumHeight(22);
    dateTimeEdit->setDisplayFormat("HH:mm:ss");

    confirm = new UPushButton(QObject::tr("confirm"), q);
    confirm->setMinimumHeight(22);
    confirm->setObjectName("CalendarConfirmButton");

    QHBoxLayout* hLayout = new QHBoxLayout;
    hLayout->addWidget(timeLabel);
    hLayout->addWidget(dateTimeEdit);
    hLayout->addStretch();
    hLayout->addWidget(confirm);
    hLayout->setSpacing(12);
    hLayout->setContentsMargins(12, 12, 12, 12);
    buttomWidget->setLayout(hLayout);

    QVBoxLayout* vBodyLayout = qobject_cast<QVBoxLayout*>(q->layout());
    vBodyLayout->addWidget(buttomWidget);
    vBodyLayout->setSpacing(0);
    vBodyLayout->setContentsMargins(0, 0, 0, 0);
}

void UCalendarWidgetPrivate::initConnnection()
{
    Q_Q(UCalendarWidget);

    QObject::connect(leftYearBtn, &UStyleButton::clicked, q, &UCalendarWidget::onDateChanged);
    QObject::connect(leftMonthBtn, &UStyleButton::clicked, q, &UCalendarWidget::onDateChanged);
    QObject::connect(rightYearBtn, &UStyleButton::clicked, q, &UCalendarWidget::onDateChanged);
    QObject::connect(rightMonthBtn, &UStyleButton::clicked, q, &UCalendarWidget::onDateChanged);
    QObject::connect(q, &QCalendarWidget::currentPageChanged, q, &UCalendarWidget::onCurrentPageChanged);
    QObject::connect(confirm, &QPushButton::clicked, q, [ = ]()
    {
        emit q->confirmClicked();
        emit q->selectDateTime(QDateTime(q->selectedDate(), dateTimeEdit->time()));
    });
    QObject::connect(&timer, &QTimer::timeout, q, [ = ]()
    {
        dateTimeEdit->setTime(QTime::currentTime());
    });
}

void UCalendarWidgetPrivate::setDataTimeText(int year, int month)
{
    yearText->setText(QString("%1").arg(year, 2, 10, QLatin1Char('0')));
    monthText->setText(QString("%1").arg(month, 2, 10, QLatin1Char('0')));
}

UCalendarWidget::UCalendarWidget(QWidget* parent) : QCalendarWidget(parent)
    , d_ptr(new UCalendarWidgetPrivate(this))
{
    Q_D(UCalendarWidget);

    d->initStyleSheet();
    d->initNavigationBar();
    d->initButtomWidget();
    d->initConnnection();
    setCurrentDateTime();
}

UCalendarWidget::~UCalendarWidget()
{
}

void UCalendarWidget::setTimeVisible(bool visible)
{
    Q_D(UCalendarWidget);
    d->dateTimeEdit->setVisible(visible);
    d->timeLabel->setVisible(visible);
}

void UCalendarWidget::setCurrentDateTime()
{
    Q_D(UCalendarWidget);

    QDateTime dataTime = QDateTime::currentDateTime();
    setSelectedDate(dataTime.date());
    d->dateTimeEdit->setTime(dataTime.time());
    d->currentMonth = dataTime.date().month();
}

void UCalendarWidget::setDateTime(const QDateTime &time)
{
    Q_D(UCalendarWidget);

    setSelectedDate(time.date());
    d->dateTimeEdit->setTime(time.time());
    d->currentMonth = time.date().month();
}

void UCalendarWidget::updateCells()
{
    QCalendarWidget::updateCells();
}

void UCalendarWidget::startTime()
{
    Q_D(UCalendarWidget);

    if (!d->timer.isActive())
        d->timer.start();
}

void UCalendarWidget::stopTime()
{
    Q_D(UCalendarWidget);

    if (d->timer.isActive())
        d->timer.stop();
}

void UCalendarWidget::paintEvent(QPaintEvent*)
{
    QPainter painter(this);
    painter.setRenderHints(QPainter::Antialiasing);

    UProxyStyle* style = qobject_cast<UProxyStyle*>(uApp->style());
    int radius = style ? style->pixelMetric(UProxyStyle::PM_FrameRadius) : 8;

    QColor color = uApp->applicationPalette()->color(QPalette::Shadow);
    QBrush brush = uApp->applicationPalette()->brush(QPalette::Base);
    UProxyStyle::qDrawPlainRoundedRect(&painter, rect(), color, 1, &brush, radius);
}

void UCalendarWidget::changeEvent(QEvent* event)
{
    Q_D(UCalendarWidget);
    switch (event->type())
    {
        case QEvent::LanguageChange:
        {
            d->yearLabel->setText(QObject::tr("year"));
            d->monthLabel->setText(QObject::tr("month"));
            d->timeLabel->setText(QObject::tr("time"));
            d->confirm->setText(QObject::tr("confirm"));
            setLocale(QLocale(uApp->currentLanguage()));
            break;
        }
        case QEvent::PaletteChange:
            d->setTextFormat();
            break;
        default:
            QCalendarWidget::changeEvent(event);
            break;
    }
}

#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
void UCalendarWidget::paintCell(QPainter* painter, const QRect &rect, const QDate &date) const
#else
void UCalendarWidget::paintCell(QPainter* painter, const QRect &rect, QDate date) const
#endif
{
    Q_D(const UCalendarWidget);
    QFont dateFont = painter->font();
    painter->setFont(dateFont);
    painter->setRenderHint(QPainter::Antialiasing);
    QPainterPath path;
    path.addRect(rect);
    painter->setClipPath(path);
    painter->fillPath(path, uApp->applicationPalette()->brush(QPalette::Base));

    if (date == selectedDate())
    {
        painter->setPen(Qt::NoPen);
        painter->setBrush(uApp->applicationPalette()->brush(QPalette::Highlight));
        painter->drawRoundedRect(rect, 4, 4);
        painter->setPen(QPen(uApp->applicationPalette()->color(QPalette::HighlightedText)));
        painter->drawText(rect, int(Qt::AlignCenter), QString::number(date.day()));
    }
    else if (date == QDate::currentDate())
    {
        painter->setPen(QPen(uApp->applicationPalette()->color(QPalette::Highlight)));
        painter->setBrush(Qt::NoBrush);
        painter->drawRoundedRect(rect, 4, 4);
        painter->setPen(QPen(uApp->applicationPalette()->color(QPalette::Highlight)));
        painter->drawText(rect, int(Qt::AlignCenter), QString::number(date.day()));
    }
    else
    {
        painter->setPen(QPen(uApp->applicationPalette()->color(QPalette::WindowText)));
        if (d->currentMonth != date.month())
        {
            painter->setPen(QPen(uApp->applicationPalette()->color(UPalette::OfflineBackground)));
        }
        painter->drawText(rect, int(Qt::AlignCenter), QString::number(date.day()));
    }
}

void UCalendarWidget::hideEvent(QHideEvent* event)
{
    QCalendarWidget::hideEvent(event);
    emit closed();
}

void UCalendarWidget::onCurrentPageChanged(int year, int month)
{
    Q_D(UCalendarWidget);

    d->setDataTimeText(year, month);
    d->currentMonth = month;
}

void UCalendarWidget::onDateChanged()
{
    Q_D(UCalendarWidget);
    UStyleButton* senderBtn = qobject_cast<UStyleButton*>(sender());
    if (senderBtn == d->leftYearBtn && QDate(yearShown() - 1, monthShown(), 1) >= minimumDate())
    {
        showPreviousYear();
    }
    else if (senderBtn == d->leftMonthBtn && QDate(yearShown(), monthShown(), 1) > minimumDate())
    {
        showPreviousMonth();
    }
    else if (senderBtn == d->rightYearBtn && QDate(yearShown() + 1, monthShown(), 1) <= maximumDate())
    {
        showNextYear();
    }
    else if (senderBtn == d->rightMonthBtn && QDate(yearShown(), monthShown(), 1) < maximumDate())
    {
        showNextMonth();
    }
}
